// 浅拷贝： 创建一个新对象，这个对象有着原始对象属性值的一份精确拷贝。
// 如果属性是基本类型，拷贝的就是基本类型的值，如果属性是引用类型，
// 拷贝的就是内存地址 ，所以如果其中一个对象改变了这个地址，
// 就会影响到另一个对象。
// Object.assign()  ...  slice

let o1 = { name: "zs", age: [1, [1, 1, 1], { country: [1, 2, 3] }] };

// let o2 = Object.assign({}, o1);
// 数组可以使用 let o2 = o1.slice()
// Array.prototype.slice.call(null)  [...arr]

// 深拷贝，不考虑函数，RegExp，Date数据类型，不考虑循环引用问题
function deepClone(source) {
  let target = Array.isArray(source) ? [] : {};

  for (let key in source) {
    if (source.hasOwnProperty(key)) {
      if (judgeObect(source[key])) {
        target[key] = deepClone(source[key]);
      } else {
        target[key] = source[key];
      }
    }
  }

  return target;
}

// 深拷贝 简单版
function deepClone3(obj, map = new WeakMap()) {
  if (obj instanceof RegExp) return new RegExp(obj);
  if (obj instanceof Date) return new Date(obj);

  if (obj == null || typeof obj != "object") return obj;
  if (map.has(obj)) {
    return map.get(obj);
  }
  let target = new obj.constructor();
  map.set(obj, t);
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      target[key] = deepClone(obj[key], map);
    }
  }
  return target;
}

// 深拷贝，循环引用问题，
// 利用了弱引用对象WeakMap，垃圾回收机制会自动帮我们回收
//使用Map的话，那么对象间是存在强引用关系的
// let obj = { name: "ConardLi" };
// const target = new Map();
// target.set(obj, "code秘密花园");
// obj = null;
// 虽然我们手动将obj，进行释放，然是target依然对obj存在强引用关系，所以这部分内存依然无法被释放。
// 2. 如果使用WeakMap的话
// let obj = { name: "ConardLi" };
// const target = new WeakMap();
// target.set(obj, "code秘密花园");
// obj = null;
// 如果是WeakMap的话，target和obj存在的就是弱引用关系，当下一次垃圾回收机制执行时，这块内存就会被释放掉
function deepClone(source, map = new WeakMap()) {
  if (isObject(source)) {
    let target = Array.isArray(source) ? [] : {};
    // 在进行克隆之前，先判断字典里面是否存在，如果有直接返回，如果没有则先存储在字典里，然后再继续克隆
    if (map.get(source)) {
      // 放在循环引用，继续向下查找
      return map.get(source);
    }

    map.set(source, target);

    for (const key in source) {
      if (isObject(source[key])) {
        target[key] = deepClone(source[key], map);
      } else {
        target[key] = source[key];
      }
    }
    return target;
  } else {
    return source;
  }
}

function isObject(source) {
  if (source && typeof source === "object") {
    return true;
  }
  return false;
}
// 判断数据类型 [object Number] [object RegExp/Date/Function/Array...]
function getType(source) {
  return Object.prototype.toString.call(source);
}
// 也可以写一个判断对象类型的函数
let isType = (type) => (obj) =>
  Object.prototype.toString.call(obj) === `[object ${type}]`;
// 根据构造函数创建对象实例，不用字面量创建
function getInit(source) {
  const Ctor = source.constructor;
  return new Ctor();
}

//-----------------------------------------------
// 高级深入，可以继续遍历的类型 Object,Array,Map,Set/ 不可以继续遍历的类型
function clone(source, map = new WeakMap()) {
  // 克隆原始类型
  if (!isObject(source)) {
    return source;
  }

  // 初始化
  // 之前是这样写的let target = Array.isArray(source) ? [] : {};
  // 等价于 new Array()， new Object()
  const type = getType(source);
  let cloneTarget;
  // 只对对象，数组，Map/Set 进行遍历
  if (
    [
      "[object Object]",
      "[object Array]",
      "[object Map]",
      "[object Set]",
    ].includes(type)
  ) {
    cloneTarget = getInit(source, type);
  } else {
    return cloneOtherType(source, type);
  }

  // 防止循环引用
  if (map.get(source)) {
    return map.get(source);
  }
  map.set(source, cloneTarget);

  // 克隆set
  if (type === "[object Set]") {
    source.forEach((value) => {
      cloneTarget.add(clone(value, map));
    });
    return cloneTarget;
  }

  // 克隆map
  if (type === "[object Map]") {
    source.forEach((value, key) => {
      cloneTarget.set(key, clone(value, map));
    });
    return cloneTarget;
  }

  // 克隆对象和数组
  const keys = type === arrayTag ? undefined : Object.keys(source);
  forEach(keys || source, (value, key) => {
    if (keys) {
      key = value;
    }
    cloneTarget[key] = clone(source[key], map);
  });

  return cloneTarget;
}

// cloneOtherType 克隆其他类型Bool、Number、String、String、Date、Error这几种类型
function cloneOtherType(source, type) {
  const constructorTmp = source.constructor;
  switch (type) {
    case boolTag:
    case numberTag:
    case stringTag:
    case errorTag:
    case dateTag:
      return new constructorTmp(source);
    case regexpTag:
      return cloneReg(source);
    case symbolTag:
      return Object(Symbol.prototype.valueOf.call(source));
    default:
      return null;
  }
}

// 克隆正则：
function cloneReg(source) {
  const reFlags = /\w*$/;
  const result = new source.constructor(source.source, reFlags.exec(source));
  result.lastIndex = source.lastIndex;
  return result;
}

const mapTag = "[object Map]";
const setTag = "[object Set]";
const arrayTag = "[object Array]";
const objectTag = "[object Object]";
const argsTag = "[object Arguments]";

const boolTag = "[object Boolean]";
const dateTag = "[object Date]";
const numberTag = "[object Number]";
const stringTag = "[object String]";
const symbolTag = "[object Symbol]";
const errorTag = "[object Error]";
const regexpTag = "[object RegExp]";
const funcTag = "[object Function]";

const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag];

// 克隆函数
function cloneFunction(func) {
  const bodyReg = /(?<={)(.|\n)+(?=})/m;
  const paramReg = /(?<=\().+(?=\)\s+{)/;
  const funcString = func.toString();
  if (func.prototype) {
    console.log("普通函数");
    const param = paramReg.exec(funcString);
    const body = bodyReg.exec(funcString);
    if (body) {
      console.log("匹配到函数体：", body[0]);
      if (param) {
        const paramArr = param[0].split(",");
        console.log("匹配到参数：", paramArr);
        return new Function(...paramArr, body[0]);
      } else {
        return new Function(body[0]);
      }
    } else {
      return null;
    }
  } else {
    return eval(funcString);
  }
}
